#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#include <colors>
#include <CSMod_IP>

#define Version "1.1"
#define CVAR_FLAGS FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_NOTIFY

const TEAM_SURVIVOR = 2;
const TEAM_INFECTED = 3;

new bool:playerbindkey[MAXPLAYERS+1] = false;
new bool:inserver[MAXPLAYERS+1] = false;

new Handle:sm_msg_system_enable = INVALID_HANDLE;
new Handle:sm_msg_system_showhp = INVALID_HANDLE;
new Handle:sm_msg_system_showhurt = INVALID_HANDLE;
new Handle:sm_msg_system_showlocation = INVALID_HANDLE;
new Handle:sm_msg_system_firefriend = INVALID_HANDLE;
new Handle:sm_msg_system_hurtman = INVALID_HANDLE;
new Handle:sm_msg_system_dead = INVALID_HANDLE;
new Handle:sm_msg_system_killer = INVALID_HANDLE;
new Handle:sm_msg_system_deadbot = INVALID_HANDLE;
new Handle:sm_msg_system_connect = INVALID_HANDLE;
new Handle:sm_msg_system_playernum = INVALID_HANDLE;
new Handle:sm_msg_system_team = INVALID_HANDLE;
new Handle:sm_msg_system_ghostmsg = INVALID_HANDLE;
new Handle:sm_msg_system_hurtmsg = INVALID_HANDLE;

// 是否每次切换队伍都显示IP信息
#define SHOW_MSG_SWITCHTEAM

new Show_Player[MAXPLAYERS+1][256];
//
new Handle:g_kvDB = INVALID_HANDLE;
//Cvars' varibles
new Handle:cvar_ar_time = INVALID_HANDLE;
new ar_time = 30;

public Plugin:myinfo =
{
    name="信息提示系统",
    author="YD社区",
    description="显示各种类型的的游戏信息.",
    version=Version,
    url="www.l4d.cn"
};

public OnPluginStart()
{
    decl String:game_name[64];
    GetGameFolderName(game_name, sizeof(game_name));
    if (!StrEqual(game_name, "left4dead", false) && !StrEqual(game_name, "left4dead2", false))
    {
        SetFailState("该插件只能用于l4d和l4d2.");
    }
    g_kvDB = CreateKeyValues("getip");
    cvar_ar_time = CreateConVar("sm_anti_reconnect_time", "120", "离开服务器后玩家需要等待多少秒才能再次进入服务器？, 0 = 禁用", FCVAR_PLUGIN, true, 0.0);
    HookConVarChange(cvar_ar_time, OnCVarChange);
    
    sm_msg_system_enable = CreateConVar("sm_msg_system_enable","1","是否开启插件(0:关闭 1:开启)", CVAR_FLAGS)
    sm_msg_system_showlocation = CreateConVar("sm_msg_system_showlocation","3","伤害显示的位置 (1:聊天框 2:提示框 3:顶部提示框)", CVAR_FLAGS)
    sm_msg_system_showhp = CreateConVar("sm_msg_system_showhp","0","攻击时是否显示对方HP(0:关闭 1:开启)", CVAR_FLAGS)
    sm_msg_system_showhurt = CreateConVar("sm_msg_system_showhurt","0","攻击时是否显示对对方的伤害(0:关闭 1:开启)", CVAR_FLAGS)
    sm_msg_system_firefriend = CreateConVar("sm_msg_system_firefriend","0","是否显示友军伤害(0:关闭 1:开启)", CVAR_FLAGS)
    sm_msg_system_hurtman = CreateConVar("sm_msg_system_hurtman","0","是否对被攻击的人显示伤害(0:关闭 1:开启)", CVAR_FLAGS)    
    sm_msg_system_dead = CreateConVar("sm_msg_system_dead","1","是否开启玩家死亡提示(0:关闭 1:只显示幸存者 2:只显示感染者 3:显示全部阵营)", CVAR_FLAGS)
    sm_msg_system_killer = CreateConVar("sm_msg_system_killer","1","开启死亡提示后,是否显示杀手是谁(0:关闭 1:开启)", CVAR_FLAGS)
    sm_msg_system_deadbot = CreateConVar("sm_msg_system_deadbot","1","显示bot死亡提示(0:关闭 1:开启)", CVAR_FLAGS)
    sm_msg_system_connect = CreateConVar("sm_msg_system_connect","1","显示玩家连接和离开的信息(0:关闭 1:开启)", CVAR_FLAGS)
    sm_msg_system_playernum = CreateConVar("sm_msg_system_playernum","1","玩家连接和断开时显示剩余玩家数(0:关闭 1:开启)", CVAR_FLAGS)
    sm_msg_system_team = CreateConVar("sm_msg_system_team","1","是否显示玩家转换队伍信息(0:关闭 1:开启)", CVAR_FLAGS)
    sm_msg_system_ghostmsg = CreateConVar("sm_msg_system_ghostmsg","1","显示Ghost按R使用超长跳跃(0:关闭 1:开启)", CVAR_FLAGS)
    sm_msg_system_hurtmsg = CreateConVar("sm_msg_system_hurtmsg","0","是否显示伤害信息(0:关闭 1:开启)", CVAR_FLAGS)
    
    
    //AutoExecConfig(true, "sm_msg_system");
    if (GetConVarInt(sm_msg_system_enable) == 1){ Enable(); }
    else { Disable(); }    
}
public OnCVarChange(Handle:convar_hndl, const String:oldValue[], const String:newValue[])
{
    GetCVars();
}
public OnConfigsExecuted()
{
    GetCVars();
}
public OnMapStart()
{
    CloseHandle(g_kvDB);
    g_kvDB = CreateKeyValues("getip");
}

public GetCVars()
{
    ar_time = GetConVarInt(cvar_ar_time);
}

//玩家被攻击
public Action:Player_Hurt(Handle:event, String:event_name[], bool:dontBroadcast) 
{ 
    //获取参数
    new userid = GetClientOfUserId(GetEventInt(event, "userid"));
    new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
    new dmg = GetEventInt(event, "dmg_health");
    new health = GetEventInt(event, "health");
    if (!userid || !attacker) { return; }
    if (!IsClientInGame(userid) || !IsClientInGame(attacker) || !IsClientConnected(userid) || !IsClientConnected(attacker)) { return; }
    if (IsFakeClient(attacker)) { return; }
    
    //判断是否友军
    if (IsValidSurvivor(userid, true) && IsValidSurvivor(attacker, true))
    {
        if (!GetConVarInt(sm_msg_system_firefriend))
        {
            return ;
        }
    }
    
    if (!IsValidSurvivor(userid, true) && !IsValidSurvivor(attacker, true))
    {
        if (!GetConVarInt(sm_msg_system_firefriend))
        {
            return ;
        }
    }


    
    //判断需要显示的类型
    if (GetConVarInt(sm_msg_system_showhp) && GetConVarInt(sm_msg_system_showhurt))
    {
                
        if (GetConVarInt(sm_msg_system_showlocation) == 1) {CPrintToChat(attacker,"你的攻击对 {green}%N {default}造成了 {olive}%d {default}伤害,他剩余: {red}%d {default}点HP", userid, dmg , health);}
        else if (GetConVarInt(sm_msg_system_showlocation) == 2) {PrintHintText(attacker,"你的攻击对 %N 造成了 %d 伤害,他剩余: %d 点HP", userid, dmg , health);}
        else if (GetConVarInt(sm_msg_system_showlocation) == 3) {PrintCenterText(attacker,"你的攻击对 %N 造成了 %d 伤害,他剩余: %d 点HP", userid, dmg , health);}
        if (GetConVarInt(sm_msg_system_hurtman) && !IsFakeClient(userid))
        {
            if (GetConVarInt(sm_msg_system_showlocation) == 1) {CPrintToChat(userid,"你被 {green}%N {default}攻击造成了 {olive}%d {default}伤害,剩余: {red}%d {default}点HP", attacker, dmg , health);}
            else if (GetConVarInt(sm_msg_system_showlocation) == 2) {PrintHintText(userid,"你被 %N 攻击造成了 %d 伤害,剩余: %d 点HP", attacker, dmg , health);}
            else if (GetConVarInt(sm_msg_system_showlocation) == 3) {PrintCenterText(userid,"你被 %N 攻击造成了 %d 伤害,剩余: %d 点HP", attacker, dmg , health);}
        }
    }
    else if (!GetConVarInt(sm_msg_system_showhp) && GetConVarInt(sm_msg_system_showhurt))
    {
        if (GetConVarInt(sm_msg_system_showlocation) == 1) {CPrintToChat(attacker,"你的攻击对 {green}%N {default}造成了 {olive}%d {default}伤害", userid, dmg);}
        else if (GetConVarInt(sm_msg_system_showlocation) == 2) {PrintHintText(attacker,"你的攻击对 %N 造成了 %d 伤害", userid, dmg);}
        else if (GetConVarInt(sm_msg_system_showlocation) == 3) {PrintCenterText(attacker,"你的攻击对 %N 造成了 %d 伤害", userid, dmg);}
        if (GetConVarInt(sm_msg_system_hurtman) && !IsFakeClient(userid))
        {
        if (GetConVarInt(sm_msg_system_showlocation) == 1) {CPrintToChat(userid,"你被 {green}%N {default}攻击造成了 {olive}%d {default}伤害", attacker, dmg);}
        else if (GetConVarInt(sm_msg_system_showlocation) == 2) {PrintHintText(userid,"你被 %N 攻击造成了 %d 伤害", attacker, dmg);}
        else if (GetConVarInt(sm_msg_system_showlocation) == 3) {PrintCenterText(userid,"你被 %N 攻击造成了 %d 伤害", attacker, dmg);}    
        }
    }
    else if (GetConVarInt(sm_msg_system_showhp) && !GetConVarInt(sm_msg_system_showhurt))
    {
        if (GetConVarInt(sm_msg_system_showlocation) == 1) {CPrintToChat(attacker,"{green}%N {default}剩余 {olive}%d {default}点HP", userid, health);}
        else if (GetConVarInt(sm_msg_system_showlocation) == 2) {PrintHintText(attacker,"%N 剩余 %d 点HP", userid, health);}
        else if (GetConVarInt(sm_msg_system_showlocation) == 3) {PrintCenterText(attacker,"%N 剩余 %d 点HP", userid, health);}
        if (GetConVarInt(sm_msg_system_hurtman) && !IsFakeClient(userid))
        {
            if (GetConVarInt(sm_msg_system_showlocation) == 1) {CPrintToChat(userid,"你受到攻击,剩余 {olive}%d {default}点HP", health);}
            else if (GetConVarInt(sm_msg_system_showlocation) == 2) {PrintHintText(userid,"你受到攻击,剩余 %d 点HP", health);}
            else if (GetConVarInt(sm_msg_system_showlocation) == 3) {PrintCenterText(userid,"你受到攻击,剩余 %d 点HP", health);}
        }
    }
    

}

//玩家死亡
public Action:Player_Death(Handle:event, String:event_name[], bool:dontBroadcast)
{    
    if (!GetConVarInt(sm_msg_system_dead)){ return; }
    new userid = GetClientOfUserId(GetEventInt(event,"userid"));
    new attacker = GetClientOfUserId(GetEventInt(event,"attacker"));
    
    if (userid && attacker && IsClientConnected(userid) && IsClientConnected(attacker) && !IsFakeClient(userid))
    {
        if (!IsFakeClient(attacker))
        {
            PrintHintText(userid, "你被┃ %N ┃搞死", attacker);
        }
        else { PrintHintText(userid, "你被【电脑】搞死"); }
    }
    
    if (GetConVarInt(sm_msg_system_dead) == 1)
    {
        if (IsValidSurvivor(userid,GetConVarBool(sm_msg_system_deadbot)))
        {
            if (GetConVarInt(sm_msg_system_killer))
            {
                if (attacker && userid != attacker)
                CPrintToChatAll("{blue}幸存者: {green}%N {default}已被 {olive}%N {default}搞死.　 (＞﹏＜)", userid, attacker);
                else {CPrintToChatAll("{blue}幸存者: {green}%N {default}已挂掉.　 (＞﹏＜)", userid, attacker);}
            }
            else {CPrintToChatAll("{blue}幸存者: {green}%N {default}已挂掉.　 (＞﹏＜)", userid);}
        }
    }
    else if (GetConVarInt(sm_msg_system_dead) == 2)
    {
        if (!IsValidSurvivor(userid,GetConVarBool(sm_msg_system_deadbot)) && !IsHangingFromLedge(userid))
        {
            if (GetConVarInt(sm_msg_system_killer)){CPrintToChatAll("{red}感染者: {green}%N {default}已被 {olive}%N {default}搞死.", userid, attacker);}
            else {CPrintToChatAll("{red}感染者: {green}%N {default}已挂掉.", userid);}
        }
    }
    else if (GetConVarInt(sm_msg_system_dead) == 3)
    {
        if (IsValidSurvivor(userid,GetConVarBool(sm_msg_system_deadbot)) )
        {
            if (GetConVarInt(sm_msg_system_killer))
            {
                if (attacker && userid != attacker)
                CPrintToChatAll("{blue}幸存者: {green}%N {default}已被 {olive}%N {default}搞死.　 (＞﹏＜))", userid, attacker);
                else {CPrintToChatAll("{blue}幸存者: {green}%N {default}已挂掉.　 (＞﹏＜)", userid, attacker);}
            }
            else {CPrintToChatAll("{blue}幸存者: {green}%N {default}已挂掉.　 (＞﹏＜)", userid);}
        }
        else if (!IsValidSurvivor(userid,GetConVarBool(sm_msg_system_deadbot)) && !IsHangingFromLedge(userid))
        {
            if (GetConVarInt(sm_msg_system_killer)){CPrintToChatAll("{red}感染者: {green}%N {default}已被 {olive}%N {default}搞死.", userid, attacker);}
            else {CPrintToChatAll("{red}感染者: {green}%N {default}已挂掉.", userid);}
        }
    }
    

}

Enable()
{
    if (GetConVarInt(sm_msg_system_hurtmsg) == 1)
    {
        HookEvent("player_hurt", Player_Hurt);
    }
    HookEvent("player_death", Player_Death);
    HookEvent("player_team",Player_Team);
    if (GetConVarInt(sm_msg_system_ghostmsg) == 1)
    {
        HookEvent("ghost_spawn_time", Ghost_Spawn_Time);
    }
}
Disable() 
{
    UnhookEvent("player_hurt", Player_Hurt);
    UnhookEvent("player_death", Player_Death);
    UnhookEvent("player_team",Player_Team);
    UnhookEvent("ghost_spawn_time",Ghost_Spawn_Time);
}

stock bool:IsValidSurvivor(client, bool:allowbots) {

    if ((client < 1) || (client > MaxClients)) { return false; }
    if (!IsClientInGame(client) || !IsClientConnected(client)) { return false; }
    if (GetClientTeam(client) != TEAM_SURVIVOR) { return false; }
    if (IsFakeClient(client) && !allowbots) { return false; }
    
    return true;
    
}

stock bool:IsHangingFromLedge(client) {

    return (GetEntProp(client, Prop_Send, "m_isHangingFromLedge") == 1);

}

//玩家连接
public OnClientPutInServer(client)
{
    if (!GetConVarInt(sm_msg_system_connect)){ return; }
    if (IsFakeClient(client)){ return; }
    
    new String:playername[32];
    GetClientName(client,playername,sizeof(playername));
    // IP
    decl String:steamId[30];    
    GetClientIP(client, steamId, sizeof(steamId), false);    
    new disconnect_time = KvGetNum(g_kvDB, steamId, -1);
    //
    decl address[17];
    GetClientIP(client,address,16);
    csmod_ip(address,Show_Player[client],255);      
    //
    if (GetConVarInt(sm_msg_system_playernum))
    {        
        if(1)
        {        
            if(disconnect_time == -1)
            {            
                CPrintToChatAll("{lightgreen}%N {default}加入游戏,服务器总人数为 {green}%i {default}人. \n{default}他来自：{green}%s", client, GetRealClientCount(true),Show_Player[client]);            
            }
            else
            {
                new wait_time = disconnect_time + ar_time - RoundToFloor(GetTickedTime());
                if (wait_time <= 0)
                {
                    KvDeleteKey(g_kvDB, steamId);
                    CPrintToChatAll("{lightgreen}%N {default}加入游戏,服务器总人数为 {green}%i {default}人. \n{default}他来自：{green}%s", client, GetRealClientCount(true), Show_Player[client]);
                    //PrintToChatAll("wait_time__%d", wait_time);
                }
                else
                {
                    CPrintToChatAll("{lightgreen}%N {default}加入游戏.", client);
                }
            }
        }
        else 
        {
            CPrintToChatAll("{lightgreen}%N {default}加入游戏.", client);
        }
    }
    else 
    {
        CPrintToChatAll("{lightgreen}%N {default}加入游戏.", client);
    }
    inserver[client] = true;
    //创建计时器检测热键绑定
    CreateTimer(5.0, bindkey, client, TIMER_REPEAT);
}

//玩家断开连接
public OnClientDisconnect(Client)
{
    if (!GetConVarInt(sm_msg_system_connect)){ return; }
    if (IsFakeClient(Client) || !IsClientInGame(Client)){ return; }

    new String:playername[32];
    GetClientName(Client,playername,sizeof(playername));
    if (GetConVarInt(sm_msg_system_playernum))
    {
        CPrintToChatAll("{lightgreen}%N {default}离开了游戏,服务器总人数 {green}%i {default}人.", Client, GetRealClientCount(true)-1);
    }
    else
    {
        CPrintToChatAll("{lightgreen}%N {default}离开了游戏.", Client);
    }
    inserver[Client] = false;
    playerbindkey[Client] = false;
    //
    decl String:steamId[30];
    GetClientIP(Client, steamId, sizeof(steamId), false);        
    KvSetNum(g_kvDB, steamId, RoundToFloor(GetTickedTime()));// 一定时间内置显示一次IP
    //
}


//玩家队伍
public Action:Player_Team(Handle:event, String:event_name[], bool:dontBroadcast)
{
    if (!GetConVarInt(sm_msg_system_team)){ return; }
    new userid =  GetClientOfUserId(GetEventInt(event,"userid"));
    if (!userid) { return; }
    if (IsFakeClient(userid)){ return; }
    if (!IsClientInGame(userid) || !IsClientConnected(userid)) { return; }
    new team = GetEventInt(event,"team")
    new oldteam = GetEventInt(event,"oldteam") 
    //PrintToChatAll("oldteam: %i   team: %i",oldteam,team);
    if (oldteam == 1)
    {
        if (team == 2) { CPrintToChatAll("{green}%N {default}从 旁观者 {default}加入到了 {olive}幸存者", userid); }
        else if (team == 3) { CPrintToChatAll("{green}%N {default}从 旁观者 {default}加入到了 {olive}感染者", userid); }
    }
    else if (oldteam == 2)
    {
        if (team == 1) { CPrintToChatAll("{green}%N {default}从 幸存者 {default}加入到了 旁观者", userid); }
        else if (team == 3) { CPrintToChatAll("{green}%N {default}从 {olive}幸存者 {default}加入到了 {olive}感染者", userid); }        
    }
    else if (oldteam == 3)
    {
        if (team == 1) { CPrintToChatAll("{green}%N {default}从 感染者 {default}加入到了 旁观者", userid); }
        else if (team == 2) { CPrintToChatAll("{green}%N {default}从 {olive}感染者 {default}加入到了 {olive}幸存者", userid); }        
    }    
    
    
}


//灵魂重生时间
public Action:Ghost_Spawn_Time(Handle:event, const String:name[], bool:dontBroadcast)
{
    new userid = GetClientOfUserId(GetEventInt(event, "userid"));
    if (IsClientInGame(userid) && !IsFakeClient(userid) && GetClientTeam(userid) == 3)
    {
        PrintHintText(userid,"感染者在灵魂状态下可以使用[R]键飞行.");
    }

}
//绑定热键
public bool:BindServerKey(Client)
{
    if (!IsClientInGame(Client) || IsFakeClient(Client)) { return false; }
    if (playerbindkey[Client]) { return false; }
    if (GetClientTeam(Client) == 0) { return false; }
    //防止玩家数字键无法使用
    ClientCommand(Client, "bind 5 slot5");
    ClientCommand(Client, "bind 6 slot6");
    ClientCommand(Client, "bind 7 slot7");
    ClientCommand(Client, "bind 8 slot8");
    ClientCommand(Client, "bind 9 slot9");
    ClientCommand(Client, "bind 0 slot10");
    
    //开始绑定
    //ClientCommand(Client, "bind b \"say !buy\"");            //暂停使用，因为2077后已经失效
    //ClientCommand(Client, "bind n \"say !wanjia\"");        //暂停使用，因为2077后已经失效
    //ClientCommand(Client, "bind k \"say !s\"");            //暂停使用，因为2077后已经失效
    //ClientCommand(Client, "bind l \"say !tp\"");            //暂停使用，因为2077后已经失效
    
    //提示
    //CPrintToChat(Client, "=========={lightgreen}服务器热键已经自动绑定{default}========= \n=================================")
    //CPrintToChat(Client, "|{blue}[B键]{olive}<购买菜单>{default}|")
    //CPrintToChat(Client, "|{blue}[K键]{olive}<技能菜单>{default}|")
    //CPrintToChat(Client, "|{blue}[N键]{olive}<玩家菜单>{default}|")
    //CPrintToChat(Client, "|{blue}[L键]{olive}<投票菜单>{default}| \n================================= \n=================================")
    //PrintHintText(Client, "============服务器热键已经自动绑定============")
    playerbindkey[Client] = true;
    return true;

}

//检测玩家是否绑定
public Action:bindkey(Handle:Timer, any:client)
{
    if (!inserver[client]) { return Plugin_Stop; }
    if (IsClientInGame(client) && !IsFakeClient(client))
    {
        if (!playerbindkey[client])
        {
            if (BindServerKey(client)) { return Plugin_Stop; }
            else { return Plugin_Continue; }
        }
    }
    return Plugin_Continue;
}

public GetRealClientCount(inGameOnly) 
{
    new clients = 0;
    if(inGameOnly)
    {
        for( new i = 1; i <= MaxClients; i++ ) 
        {
            if(IsValidPlayer(i, false))
            {
                clients++;
            }
        }            
    }
    else
    {
        for( new i = 1; i <= MaxClients; i++ ) 
        {
            if(IsClientConnected( i ) && !IsFakeClient( i ))
            {
                clients++;
            }
        }
    }
    return clients;
}

/* 判断玩家是否有效 */
stock bool:IsValidPlayer(Client, bool:AllowBot = true, bool:AllowDeath = true)
{
    if (Client < 1 || Client > MaxClients)
        return false;
    if (!IsClientConnected(Client) || !IsClientInGame(Client))
        return false;
    if (!AllowBot)
    {
        if (IsFakeClient(Client))
            return false;
    }

    if (!AllowDeath)
    {
        if (!IsPlayerAlive(Client))
            return false;
    }    
    
    return true;
}
